/*
 * Project CelestialBeing: unique_serialization.hpp
 * Created: 2024/2/8
 * This program uses MulanPSL2. See LICENSE for more.
 */

#ifndef CELESTIALBEING_UNIQUE_SERIALIZATION_H
#define CELESTIALBEING_UNIQUE_SERIALIZATION_H
#include "cb/common/celestial_being_base.hpp"
#include "cb/utility/unique_object.hpp"
#include "cb/utility/serialization.hpp"
CB_NAMESPACE_BEGIN

// Special methods to serialize and deserialize a unique & serializable object
// The unique object can only be held with std::shared_ptr.
// Serialize the pointer is regarded as serializing the object as well as its unique object id.
// Deserialize the pointer is regarded as creating and deserializing the object with the same unique object id.

template<typename T> concept CUniqueSerializable =
std::is_base_of_v<UniqueObject, std::remove_cvref_t<T>>
&& std::is_base_of_v<Serializable, std::remove_cvref_t<T>>;

// Serialize a unique object pointed by the pointer. If it is null, serialize a mark for invalid.
template<CUniqueSerializable T> inline void SerializeUniqueObject (std::ostream & os, const std::shared_ptr<T> & ptr) {
    if(ptr) {
        Serialize(os, ptr->id_);
        Serialize(os, *ptr);
    } else {
        Serialize(os, UniqueObjectID::kInvalid);
    }
}

// Create and deserialize a unique object to a pointer. It has the same unique object id as specified by the serialized bytestream.
template<CUniqueSerializable T> inline void DeserializeUniqueObject (std::istream & is, std::shared_ptr<T> & ptr) {
    UniqueObjectID id;
    Deserialize(is, id);
    if(id == UniqueObjectID::kInvalid) {
        ptr = nullptr;
    }
    assert(!UniqueObjectRegistry::Get().Exists(id) && "Can not deserialize an existing unique object.");
    ptr = UniqueObjectRegistry::Get().CreateAndRegisterObjectWithID<T>(id);
    Deserialize(is, *ptr);
}

CB_NAMESPACE_END
#endif //CELESTIALBEING_UNIQUE_SERIALIZATION_H
